#pragma once
// ==================包含板上外设头文件====================
#include "hal/delay.hpp"
#include "hal/gpio.hpp"
#include "hal/register.hpp"
#include "hal/spi.hpp"
#include "ti_msp_dl_config.h"
#include <hal/dma.hpp>
// ==================包含板上外设头文件====================
#include <array>
// ==================== 属性 ====================

// ==================随配置变动部分====================
// 引脚 这两个一定要记得改
using LCD_RST = hal::gpio<GPIOA_BASE, LCD_RST_PIN>; // 复位引脚
using LCD_RS = hal::gpio<GPIOA_BASE, LCD_RS_PIN>; // 命令数据引脚 高电平为数据
using spi = hal::spi0;
// ==================随配置变动部分====================


// =========== 配置信息 ============
namespace bsp::detail_lcd
{
    static inline constexpr uint16_t LCD_WIDTH = 480;
    static inline constexpr uint16_t LCD_HEIGHT = 320;


// DMA传输配置
    enum class DMAConfigType
    {
        disable, // 不使用DMA传输
        enable,  // 使用DMA传输
    };

    enum class Rotation
    {
        Deg0, Deg90, Deg180, Deg270
    };

// 基础操作模板
    struct LCDOperations
    {
        template<uint8_t cmd>
        static inline void write_cmd()
        {
            spi::cs_low();
            LCD_RS::low(); // 低电平发送命令
            spi::send_byte(cmd);
            spi::cs_high();
        }

        template<uint8_t data>
        static inline void write_data()
        {
            spi::cs_low();
            LCD_RS::high(); // 高电平发送数据
            spi::send_byte(data);
            spi::cs_high();
        }

        // -------- 运行时函数 --------
        static inline void write_cmd(const uint8_t cmd)
        {
            spi::cs_low();
            LCD_RS::low(); // 低电平发送命令
            spi::send_byte(cmd);
            spi::cs_high();
        }

        static inline void write_data(const uint8_t data)
        {
            spi::cs_low();
            LCD_RS::high(); // 高电平发送数据
            spi::send_byte(data);
            spi::cs_high();
        }

        template<size_t Ms>
        static void delay()
        { hal::delay::ms(Ms); }
    };

// 命令序列生成器
    template<typename... Commands>
    struct CommandSequence
    {
        template<typename Ops>
        // 添加模板参数
        static void execute()
        {
            (Commands::template execute<Ops>(), ...);
        }
    };

// 基本命令类型
    template<uint8_t Reg>
    struct RegCommand
    {
        template<typename Ops>
        static void execute()
        {
            Ops::template write_cmd<Reg>();
        }
    };

    template<uint8_t Reg, uint8_t... Data>
    struct RegWithDataCommand
    {
        template<typename Ops>
        static void execute()
        {
            Ops::template write_cmd<Reg>();
            (Ops::template write_data<Data>(), ...);
        }
    };

    template<size_t Ms>
    struct DelayCommand
    {
        template<typename Ops>
        static void execute()
        { Ops::template delay<Ms>(); }
    };

// =========== 设备初始化序列特化 ============
// ============ ST7796初始化序列 ============
    struct InitSequence
    {
        using type = CommandSequence<
                RegCommand<0x11>, // 退出睡眠模式
                DelayCommand<120>,
                RegWithDataCommand<0x36, 0x48>,       // 屏幕方向设置（1<<6|1<<3）
                RegWithDataCommand<0x3A, 0x55>,       // RGB565接口格式
                RegWithDataCommand<0xF0, 0xC3>,       // 命令集控制1
                RegWithDataCommand<0xF0, 0x96>,       // 命令集控制2
                RegWithDataCommand<0xB4, 0x01>,       // 显示反转控制
                RegWithDataCommand<0xB7, 0xC6>,       // 门驱动控制
                RegWithDataCommand<0xC0, 0x80, 0x45>, // 电源控制1（AVDD/VCL升压电压）
                RegWithDataCommand<0xC1, 0x13>,       // 电源控制2（VGH/VGL电压）
                RegWithDataCommand<0xC2, 0xA7>,       // 电源控制3（VCOM偏移）
                RegWithDataCommand<0xC5, 0x0A>,       // VCOM控制（0x0A电压值）
                RegWithDataCommand<0xE8, 0x40, 0x8A, 0x00, 0x00, 0x29, 0x19, 0xA5,
                        0x33>, // 面板配置
                RegWithDataCommand<0xE0, 0xD0, 0x08, 0x0F, 0x06, 0x06, 0x33, 0x30, 0x33,
                        0x47, 0x17, 0x13, 0x13, 0x2B, 0x31>, // 正伽马校正
                RegWithDataCommand<0xE1, 0xD0, 0x0A, 0x11, 0x0B, 0x09, 0x07, 0x2F, 0x33,
                        0x47, 0x38, 0x15, 0x16, 0x2C, 0x32>, // 负伽马校正
                RegWithDataCommand<0xF0, 0x3C>,                         // 命令集控制3
                RegWithDataCommand<0xF0, 0x69>, // 命令集控制4 + 120ms延迟
                DelayCommand<120>,
                RegCommand<0x21>, // 反色关闭
                RegCommand<0x29>  // 显示开启
        >;
    };
} // namespace bsp::detail_lcd

// ==================== 导出接口 ====================
namespace bsp
{
// DMA回调函数类型定义
    using DMACallback = void (*)(void *context);

    template<void (*delay_ms)(uint32_t), detail_lcd::DMAConfigType DMAConfig =
    detail_lcd::DMAConfigType::disable>
    class lcd
    {
        using Ops = detail_lcd::LCDOperations;
        using InitSeq = typename detail_lcd::InitSequence::type;
        // 静态成员初始化
        // DMA相关静态成员
        static inline DMACallback current_callback_ = nullptr;
        static inline void *current_context_ = nullptr;

    public:
        static inline volatile bool dma_busy_ = false;

        static void init()
        {
            LCD_RST::high();
            // LCD 复位
            delay_ms(30);
            LCD_RST::low();
            delay_ms(100);
            LCD_RST::high();
            delay_ms(50);

            // 执行编译期生成的初始化序列
            InitSeq::template execute<Ops>();

            set_rotation(detail_lcd::Rotation::Deg0);
            if constexpr (DMAConfig == detail_lcd::DMAConfigType::enable)
            {
                init_dma();
            }
        }
        /**
         * @brief 初始化LCD控制器的DMA中断
         * @note  TI的初始化工具有bug,无法生成DL_SPI_enableDMATransmitEvent
         */
        static inline void init_dma() noexcept
        {
            //只要用到中断就应该有enable_irq
            spi ::enable_irq();
            spi ::enable_dma_transmitevent();
        }
        /**
         * 设置LCD显示方向
         * 该函数用于设置LCD的显示方向，通过指定显示方向参数来确定LCD的显示方式
         * @param rot 显示方向参数，取值范围为0~3，分别表示0度、90度、180度、270度
         */
        static void set_rotation(const detail_lcd::Rotation rot)
        {
            // detail::RotationControl<DevType>::set(rot);
            // 初始为竖屏
            // 定义液晶屏顺时针旋转方向
            // 0-0度旋转，1-90度旋转，2-180度旋转，3-270度旋转
            detail_lcd::LCDOperations::write_cmd(0x36);
            // 注意：原始方向为竖屏
            //     位	名称	描述
            //     7	    MY	垂直方向镜像（1：镜像，0：正常）
            //     6	    MX	水平方向镜像（1：镜像，0：正常）
            //     5	    MV	行列交换（1：交换，0：不交换）
            //     4   	ML	垂直刷新顺序（1：反向，0：正常）
            //     3 	BGR	RGB/BGR 顺序（1：BGR，0：RGB）
            //     2 	MH	水平刷新顺序（1：反向，0：正常）
            //     1 	-	保留位
            //     0 	-	保留位
            switch (rot)
            {
                default:
                case detail_lcd::Rotation::Deg0:
                    detail_lcd::LCDOperations::write_data((1 << 5) | (1 << 6) | (1 << 7) |
                                                          (1 << 3)); // 横屏1; 0度
                    break;
                case detail_lcd::Rotation::Deg90:
                    detail_lcd::LCDOperations::write_data((1 << 6) | (1 << 3)); // 竖屏1; 90度
                    break;
                case detail_lcd::Rotation::Deg180:
                    detail_lcd::LCDOperations::write_data((1 << 5) |
                                                          (1 << 3)); // 横屏2; 180度
                    break;
                case detail_lcd::Rotation::Deg270:
                    detail_lcd::LCDOperations::write_data((1 << 7) |
                                                          (1 << 3)); // 竖屏2; 270度
                    break;
            }
        }

        static void flush(const uint16_t x1, const uint16_t y1, const uint16_t x2,
                          const uint16_t y2, uint8_t *colors)
        {
            if constexpr (DMAConfig == detail_lcd::DMAConfigType::enable)
            {
                // 使用DMA的情况
                //                while (HAL_DMA_GetState(&spi::hdma_spi2_tx) !=
                //                HAL_DMA_STATE_READY) {}
                // 检查DMA是否空闲
                while (dma_busy_){}
                dma_busy_ = true;
            }

            set_window(x1, y1, x2, y2);
            Ops::write_cmd<0x2C>(); // 开始传输数据到LCD
            const uint32_t pixel_count = (x2 - x1 + 1) * (y2 - y1 + 1); // 计算像素数量

            // ====================== SPI接口 ======================
            LCD_RS::high();
            spi::cs_low();
            if constexpr (DMAConfig == detail_lcd::DMAConfigType::disable)
            {
                for (uint32_t i = 0; i < pixel_count; ++i)
                {
                    spi::send_byte(colors[i] >> 8);   // 发送高字节
                    spi::send_byte(colors[i] & 0xFF); // 发送低字节
                }
                spi::cs_high(); // 禁用LCD片选
            }
            else
            {

                const uint16_t transfer_size = pixel_count * 2; // 每个像素2字节
                // ====== 使用DMA中断传输 =====
                // 配置DMA传输
                DL_DMA_setSrcAddr(DMA, hal::details_dma::LCD_DMA_CHAN_ID, (uint32_t) colors);
                DL_DMA_setDestAddr(DMA, hal::details_dma::LCD_DMA_CHAN_ID, (uint32_t) &SPI_AAA_INST->TXDATA);
                DL_DMA_setTransferSize(DMA, hal::details_dma::LCD_DMA_CHAN_ID, transfer_size);
                DL_DMA_enableChannel(DMA, hal::details_dma::LCD_DMA_CHAN_ID);

                spi::cs_high(); // 禁用LCD片选
            }
        }

        static void write_pixel(const uint16_t x, const uint16_t y,
                                const uint16_t color)
        {
            set_window(x, y, x, y);
            Ops::write_cmd<0x2C>(); // 开始传输数据到LCD
            LCD_RS::high();

            spi::cs_low();
            spi::send_byte(color >> 8);   // 发送高字节
            spi::send_byte(color & 0xFF); // 发送低字节
            spi::cs_high();               // 禁用LCD片选
        }

        /**
         * 清屏函数
         * 该函数用于清屏，将LCD的显示区域清空为指定的颜色
         * @param color 要清屏的背景颜色
         * @note 使用这个函数的前提是，sysconfig中address mode要设置为 Fixed addr to Fixed addr
         */
        static void clear(uint8_t color)
        {

            if constexpr (DMAConfig == detail_lcd::DMAConfigType::enable)
            {
                static constexpr uint16_t lines = 64;// 一次绘制的行数

                for (int i = 0; i < 5; ++i)
                {
                    while (dma_busy_){}
                    dma_busy_ = true;
                    const uint32_t pixel_count = 480 * lines; // 计算像素数量
                    set_window(0, i * lines, 479, (i + 1) * lines);
                    Ops::write_cmd<0x2C>(); // 开始传输数据到LCD
                    // 保存颜色字节
                    // ====================== SPI接口 ======================
                    LCD_RS::high();
                    spi::cs_low();
                    static uint16_t color_byte = color;
                    // 计算并设置传输大小
                    const uint16_t transfer_size = pixel_count * 2;
                    DL_DMA_setSrcAddr(DMA, hal::details_dma::LCD_DMA_CHAN_ID, (uint32_t) &color_byte); // color_low_byte_
                    DL_DMA_setDestAddr(DMA, hal::details_dma::LCD_DMA_CHAN_ID, (uint32_t) &SPI_AAA_INST->TXDATA);
                    DL_DMA_setTransferSize(DMA, hal::details_dma::LCD_DMA_CHAN_ID, transfer_size);
                    DL_DMA_enableChannel(DMA, hal::details_dma::LCD_DMA_CHAN_ID);

                    spi::cs_high(); // 禁用LCD片选
                }
            }
            else
            {
                const uint32_t pixel_count = 480 * 64; // 计算像素数量
                for (uint32_t i = 0; i < pixel_count; ++i)
                {
                    spi::send_byte(color >> 8); // 发送高字节
                    spi::send_byte(color & 0xFF); // 发送低字节
                }
            }

        }


        // 处理DMA完成中断的静态方法
        static void handle_dma_complete()
        {
            if constexpr (DMAConfig == detail_lcd::DMAConfigType::enable)
            {
                // 清理SPI状态
                spi::disable_dma_transmitevent();
                spi::cs_high();

                // 更新状态
                dma_busy_ = false;

                // 调用回调函数（如果存在）
                if (current_callback_)
                {
                    current_callback_(current_context_);
                }
            }
        }

        static void set_callback(DMACallback callback = nullptr,
                                 void *context = nullptr)
        {
            // 保存回调信息
            current_callback_ = callback;
            current_context_ = context;
        }

    private:
        static inline void set_window(const uint16_t x1, const uint16_t y1,
                                      const uint16_t x2, const uint16_t y2)
        {
            detail_lcd::LCDOperations::write_cmd(0x2A);
            detail_lcd::LCDOperations::write_data(x1 >> 8);
            detail_lcd::LCDOperations::write_data(x1 & 0xFF);
            detail_lcd::LCDOperations::write_data(x2 >> 8);
            detail_lcd::LCDOperations::write_data(x2 & 0xFF);

            detail_lcd::LCDOperations::write_cmd(0x2B);
            detail_lcd::LCDOperations::write_data(y1 >> 8);
            detail_lcd::LCDOperations::write_data(y1 & 0xFF);
            detail_lcd::LCDOperations::write_data(y2 >> 8);
            detail_lcd::LCDOperations::write_data(y2 & 0xFF);


        }
    };
} // namespace bsp
